package com.wsq.function;

import java.text.ParseException;
import java.util.regex.Pattern;

import org.javia.arity.Function;
import org.javia.arity.Symbols;
import org.javia.arity.SyntaxException;
import org.javia.arity.Util;

import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.KeyEvent;
import android.widget.EditText;
import android.widget.TextView;

import com.wsq.calculate.R;
import com.wsq.ui.CalculateDisplay;

public class DisplayLogic implements Parcelable {

	private CalculateDisplay display;
	private TextView sOperatorView;
	private TextView mDegree;
	private Context context;
	public CalculateSimpleEngine mSimpleEngine;
	public SimpleLogic mSimpleLogic;

	private boolean keepExpress = false;

	private static final String KEEP_CHARS = "0123456789.,";

	public String errorString;

	public boolean isKeepExpress() {
		return keepExpress;
	}

	public void setKeepExpress(boolean keepExpress) {
		this.keepExpress = keepExpress;
	}

	private String mResult = "";
	public int mLineLength = 0;
	private static int LEN_UNLIMITED = 100;

	private boolean mIsError;

	static final char MINUS = '\u2212';

	public DisplayLogic(Context context, CalculateDisplay display,
			TextView mOperator, TextView degree) {
		this.context = context;
		this.display = display;
		this.mDegree = degree;
		sOperatorView = mOperator;
		mSimpleEngine = new CalculateSimpleEngine();
		mSimpleLogic = new SimpleLogic();
		errorString = context.getResources().getString(R.string.result_error);
		this.display.setLogic(this);
	}

	public DisplayLogic() {
		// TODO Auto-generated constructor stub
	}

	public void setDisplay4ConfigurationChange(CalculateDisplay display,
			TextView operatorView, TextView degree) {
		sOperatorView = operatorView;
		this.display = display;
		this.mDegree = degree;
		this.display.setLogic(this);
	}

	public void setLinelength(int lineLength) {
		mLineLength = lineLength;

	}

	public void insert(String add) {
		if (keepExpress) {
			display.insert(add);
		} else {
			try {
				display.handle(add);
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				mResult = errorString;
				display.setText(mResult);
				mSimpleLogic.clearCalculate();
			} catch (SyntaxException e) {
				// TODO Auto-generated catch block
				mResult = errorString;
				display.setText(mResult);
				mSimpleLogic.clearCalculate();
			} catch (NumberFormatException e) {
				mResult = errorString;
				display.setText(mResult);
				mSimpleLogic.clearCalculate();
			}
		}
	}

	public boolean shouldClear(String delta) {
		String text = getText();
		return text.equals(mResult) && delta.length() == 1
				&& Character.isDigit(delta.charAt(0));
	}

	public void setResult(String result) {
		mResult = result;
	}

	public boolean acceptInsert(String delta) {
		String text = getText();
		return !mIsError
				&& (!mResult.equals(text) || isOperator(delta) || display
						.getSelectionStart() != text.length());
	}

	public boolean filterFirstChar(String delta) {
		String text = getText();
		return (text.equals("") || text.equals(errorString))
				&& operatorFilter(delta);
	}

	private String getText() {
		// TODO Auto-generated method stub
		return display.getText().toString();
	}

	// Dpad down
	public void onDown() {

	}

	// Dpad up
	public void onUp() {

	}

	// Dpad enter,calculate
	public void onEnter() {

	}

	public void onDelete() {
		if (keepExpress) {
			if (getText().equals(mResult) || mIsError) {
				clear(false);
			} else {
				// �ɷ�DEL���KEYCODE_DEL(���������ɾ�����)
				display.dispatchKeyEvent(new KeyEvent(0, KeyEvent.KEYCODE_DEL));
				mResult = "";
			}
		} else {
			display.delete();
		}
	}

	private void clear(boolean scroll) {
		display.setText("0", scroll ? CalculateDisplay.Scroll.UP
				: CalculateDisplay.Scroll.NONE);
		cleared();
	}

	// ��ӦCE����C,��ʱͳһ����
	public void onClear() {
		if (keepExpress) {

		} else {
			clearSimple();
		}
	}

	private void clearSimple() {
		display.setText("0");
		display.mSimpleLogic.clearCalculate();
		cleared();
	}

	public void cleared() {
		// TODO Auto-generated method stub
		mResult = "";
		mIsError = false;
		// updateHistory();
	}

	static boolean operatorFilter(String text) {
		return text.length() == 1 && isOperatorFilter(text.charAt(0));
	}

	static boolean isOperator(String text) {
		return text.length() == 1 && isOperator(text.charAt(0));
	}

	static boolean isOperatorFilter(char c) {
		// plus minus times div
		return "+\u00d7\u00f7/*".indexOf(c) != -1;
	}

	static boolean isOperator(char c) {
		// plus minus times div
		return "+\u2212\u00d7\u00f7/*".indexOf(c) != -1;
	}

	public boolean eatHorizontalMove(boolean toLeft) {
		EditText editText = display.getEditText();
		int cursorPos = editText.getSelectionStart();
		return toLeft ? cursorPos == 0 : cursorPos >= editText.length();
	}

	private String sizeTruncate(String str, int maxLength) {
		if (maxLength == LEN_UNLIMITED) {
			return str;
		}
		int ePos = str.lastIndexOf('E');
		if (ePos == -1) {
			ePos = str.lastIndexOf("e");
		}
		String tail = (ePos != -1) ? str.substring(ePos) : "";
		int tailLen = tail.length();
		int headLen = str.length() - tailLen;
		int maxHeadLen = maxLength - tailLen;
		int keepLen = Math.min(headLen, maxHeadLen);
		if (keepLen < 1
				|| (keepLen < 2 && str.length() > 0 && str.charAt(0) == '-')) {
			return str; // impossible to truncate
		}
		int dotPos = str.indexOf('.');
		if (dotPos == -1) {
			dotPos = headLen;
		}
		if (dotPos > keepLen) {
			int exponent = (ePos != -1) ? Integer.parseInt(str
					.substring(ePos + 1)) : 0;
			int start = str.charAt(0) == '-' ? 1 : 0;
			exponent += dotPos - start - 1;
			String newStr = str.substring(0, start + 1) + '.'
					+ str.substring(start + 1, headLen) + 'E' + exponent;
			return sizeTruncate(newStr, maxLength);

		}
		return str.substring(0, keepLen) + tail;
	}

	/**
	 * Windows style.
	 * 
	 * @author Dave
	 * 
	 */
	public class SimpleLogic implements Parcelable {
		private StringBuffer sbBuffer;
		public String equalSuffix;
		private String mOperator;

		private Symbols symbols = new Symbols();
		// NumberFormat format = NumberFormat.getInstance();

		public boolean lastIsOperator = false;

		public SimpleLogic() {
			sbBuffer = new StringBuffer();
		}

		public void addOperand(String temp) {
			sbBuffer.append(temp);
		}

		public void resetOperator(String operator) {
			int index = sbBuffer.indexOf(mOperator);
			if (index != -1) {
				sbBuffer.replace(index, sbBuffer.length(), operator);
				sOperatorView.setText(sbBuffer.toString());
				mOperator = operator;
			}
		}

		public void addOperator(String operator) {
			mOperator = operator;
			sbBuffer.append(operator);
			lastIsOperator = true;
			sOperatorView.setText(sbBuffer.toString());
		}

		public boolean isValidOperator(String operator) {
			return "+-\u2212\u00d7\u00f7/*Mod^root".indexOf(operator) != -1;
		}

		public boolean isValidDigits(String temp) {
			return Pattern.compile("[\\d|\\.|\\,|e]+").matcher(temp).find();
		}

		public boolean isEqualOperator(String temp) {
			return "=".equals(temp);
		}

		public String translate(String current) throws SyntaxException {
			return Util.doubleToString(symbols.eval(current), mLineLength, 1);
		}

		public String calculateWithEqualSuffix(String current)
				throws SyntaxException {
			return Util.doubleToString(symbols.eval(current + equalSuffix),
					mLineLength, 1);
		}

		public String calculate() throws SyntaxException {
			String temp = sbBuffer.toString();
			int index = temp.indexOf("root");
			if (index != -1) {
				String[]array=temp.split("root");
				String prefix = array[0];
				String suffix = array[1];
				temp=prefix+"^"+"(1/"+suffix+")";
			}
			temp = temp.replace("Mod", "#");
			return Util.doubleToString(symbols.eval(temp), mLineLength, 1);
		}

		public String calculateForEqual() throws SyntaxException {
			String temp = sbBuffer.toString();
			int index = temp.indexOf("root");
			if (index != -1) {
				String[]array=temp.split("root");
				String prefix = array[0];
				String suffix = array[1];
				temp=prefix+"^"+"(1/"+suffix+")";
			}
			temp = temp.replace("Mod", "#");
			return Util.doubleToString(symbols.eval(temp),
					mLineLength, 1);
		}

		public String calculate(String expression) throws SyntaxException {
			return Util
					.doubleToString(symbols.eval(expression), mLineLength, 1);
		}

		public String calculate(String expression, String arg)
				throws SyntaxException, NumberFormatException {
			double value = Double.parseDouble(arg);
			Function function = symbols.compile(expression);
			return Util.doubleToString(function.eval(value), mLineLength, 1);
		}

		public void setEqualSuffix() {
			String temp = sbBuffer.toString();
			equalSuffix = temp
					.replaceAll(
							".*([+|\\-|\u2212|\u00d7|\u00f7|/|\\*|Mod|^|root].*)",
							"$1");
		}

		public void clearEqualSuffix() {
			equalSuffix = null;
		}

		public void afterCalculate(String result, String operator) {
			sbBuffer = new StringBuffer();
			sbBuffer.append(result);
			sbBuffer.append(operator);
			lastIsOperator = true;
			sOperatorView.setText(sbBuffer.toString());
		}

		public void clearCalculate() {
			sbBuffer = new StringBuffer();
			lastIsOperator = false;
			sOperatorView.setText("");
		}

		public boolean canCalculate() {
			return canCalculate(sbBuffer.toString());
		}

		public boolean canCalculateEqual(String current) {
			return canCalculate(new StringBuffer().append(sbBuffer)
					.append(current).toString());
		}

		private boolean canCalculate(String test) {
			return Pattern
					.matches(
							"[-|\u2212]?\\d+(\\.\\d+)?([e|E][\\-|\u2212]?\\d+)?(\\+|\\-|\u2212|\u00d7|\u00f7|/|\\*|Mod|root)\\d+(\\.\\d+)?([e|E][\\-|\u2212]?\\d+)?",
							test);
		}

		public String translateInput2Result(String delta, String currentShowing)
				throws SyntaxException, NumberFormatException {
			if ("x²".equals(delta)) {
				return calculate(currentShowing + "^2");
			} else if ("x³".equals(delta)) {
				return calculate(currentShowing + "^3");
			} else if ("1/x".equals(delta) || "x*π".equals(delta)) {
				return calculate(delta, currentShowing);
			} else if ("π".equals(delta) || "E".equals(delta)) {
				return calculate(delta.toLowerCase());
			} else if ("n!".equals(delta)) {
				return calculate(currentShowing + "!");
			} else if ("Int".equals(delta)) {
				double value = Double.parseDouble(currentShowing);
				value = Math.floor(value);
				return sizeTruncate(String.valueOf(value), mLineLength);
			} else if ("Frac".equals(delta)) {
				double value = Double.parseDouble(currentShowing);
				double intValue = Math.floor(value);
				value -= intValue;
				return sizeTruncate(String.valueOf(value), mLineLength);
			} else if ("e^x".equals(delta)) {
				return calculate("e^" + currentShowing);
			} else if ("10^x".equals(delta)) {
				return calculate("10^" + currentShowing);
			} else if ("%".equals(delta)) {
				return calculate(currentShowing + "%");
			} else {
				return calculate(delta + "(" + currentShowing + ")");
			}
		}

		public final Parcelable.Creator<SimpleLogic> CREATOR = new Creator<SimpleLogic>() {

			@Override
			public SimpleLogic[] newArray(int size) {
				// TODO Auto-generated method stub
				return new SimpleLogic[size];
			}

			@Override
			public SimpleLogic createFromParcel(Parcel source) {
				// TODO Auto-generated method stub
				SimpleLogic logic = new SimpleLogic();
				logic.sbBuffer = (StringBuffer) source.readSerializable();
				logic.equalSuffix = source.readString();
				logic.lastIsOperator = (Boolean) source.readSerializable();
				return logic;
			}
		};

		@Override
		public int describeContents() {
			// TODO Auto-generated method stub
			return 0;
		}

		@Override
		public void writeToParcel(Parcel dest, int flags) {
			// TODO Auto-generated method stub
			dest.writeSerializable(sbBuffer);
			dest.writeString(equalSuffix);
			dest.writeSerializable(lastIsOperator);
		}

	}

	public static final Parcelable.Creator<DisplayLogic> CREATOR = new Creator<DisplayLogic>() {

		@Override
		public DisplayLogic[] newArray(int size) {
			// TODO Auto-generated method stub
			return new DisplayLogic[size];
		}

		@Override
		public DisplayLogic createFromParcel(Parcel source) {
			// TODO Auto-generated method stub
			DisplayLogic logic = new DisplayLogic();
			logic.mSimpleLogic = source.readParcelable(null);
			logic.errorString = source.readString();
			logic.mResult = source.readString();
			logic.mIsError = (Boolean) source.readSerializable();
			return logic;
		}
	};

	@Override
	public int describeContents() {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public void writeToParcel(Parcel dest, int flags) {
		// TODO Auto-generated method stub
		dest.writeParcelable(mSimpleLogic, flags);
		dest.writeString(errorString);
		dest.writeString(mResult);
		dest.writeSerializable(mIsError);
	}

}
